#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <cstring>
#include "logtxt.hpp"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>

Log lg;
static const uint16_t DefaultPort = 8888;
static int backlog = 10;
static const uint32_t fd_max_num = sizeof(fd_set) * 8;
static const int Defaultfd = -1;
enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR
};
class SelectServer
{
private:
    int fd_array[fd_max_num]; // 用于保存被监听的文件描述符
    int _listensocket;
    uint16_t _port;

public:
    SelectServer(uint16_t port = DefaultPort) : _port(port), _listensocket(0)
    {
        for (int i = 0; i < fd_max_num; i++)
        {
            fd_array[i] = Defaultfd; // 将所有的文件描述符初始化为-1;
        }
    }
    void Init()
    {
        _listensocket = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensocket < 0)
        {
            lg(fatal, "socket fail error:%d,strerror:%s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        lg(Info, "socket creat success,sockfd:%d", _listensocket);

        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(_port);
        socklen_t len = sizeof(server);

        int n = bind(_listensocket, (struct sockaddr *)&server, len);
        if (n < 0)
        {
            lg(fatal, "binf fail,errno:%d,strerror:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "socket bind success,sockfd:%d", _listensocket);

        if (listen(_listensocket, backlog) < 0) // 将主动文件描述符转化为被动描述符，监听连接请求
        {
            lg(fatal, "listen error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        lg(Info, "listen success,errno:%d,strerror:%s", errno, strerror(errno));
    }
    void Accepter()
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        socklen_t len = sizeof(peer);
        int sock = accept(_listensocket, (struct sockaddr *)&peer, &len);
        if (sock < 0)
        {
            lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
            return;
        }
        lg(Info, "accept success,sock:%d", sock);
        int pos = 0;

        for (pos; pos < fd_max_num; pos++)
        {
            if (fd_array[pos] != Defaultfd)
                continue;
            else
            {
                break;
            }
        }
        if (pos == fd_max_num)
        {
            lg(Warning, "fd_set is full,close sock: %d", sock);
            close(sock);
        }
        else
        {
            fd_array[pos] = sock;
            PrintFd();
        }
    }
    void Recv(int fd, int i)
    {

        char buffer[1024];

        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);

        if (n > 0)
        {

            buffer[n] = 0;
            cout << "get a message: " << buffer << endl;
        }
        else if (n == 0)
        {
            cout << "client quit" << endl;
            close(fd);
            fd_array[i] = Defaultfd; // 本质从select中移除
        }
        else
        {
            lg(Warning, "recv error, fd is:%d", fd);
            close(fd);
            fd_array[i] = Defaultfd;
        }
    }
    void PrintFd()
    {
        cout << "online fd list: ";
        for (int i = 0; i < fd_max_num; i++)
        {
            if (fd_array[i] == Defaultfd)
                continue;
            cout << fd_array[i] << " ";
        }
        cout << endl;
    }
    void HanderEvent(fd_set &reads)
    {

        for (int i = 0; i < fd_max_num; i++)
        {
            int fd = fd_array[i];
            if (fd == Defaultfd)
                continue;

            if (FD_ISSET(fd, &reads)) // 读事件就绪，需要判断是连接时间就绪，还是其他事件就绪
            {
                if (fd == _listensocket)
                {
                    Accepter();
                }
                else
                {
                    Recv(fd, i);
                }
            }
        }
    }
    void Start()
    {

        fd_array[0] = _listensocket;
        for (;;)
        {
            fd_set reads;
            FD_ZERO(&reads);
            int maxfd = fd_array[0];
            for (int i = 0; i < fd_max_num; i++) // 第一次循环，将需要监听的读文件描述符加入reads; 每次重新设定文件描述符
            {
                if (fd_array[i] == Defaultfd)
                    continue;
                FD_SET(fd_array[i], &reads);
                if (fd_array[i] > maxfd)
                {
                    maxfd = fd_array[i];
                    lg(Info, "max fd update,max fd is:%d ", maxfd);
                }
            }

            struct timeval timeout = {1, 0};
            //因为reads是一个输入输出型参数，因为每次内核向用户提交都会将没有准备好的事件覆盖，因此每次都需要重置事件位图
            int n = select(maxfd + 1, &reads, nullptr, nullptr, nullptr);
            // 缺点、
            // 1、每次需要手动设置fd描述符集合
            // 2、需要多次把用户态fd拷贝到内核态
            // 3、每次调用select都需要在内核遍历传进来的fd
            switch (n)
            {
            case 0:
                cout << "time out, timeout: " << timeout.tv_sec << "." << timeout.tv_usec << endl;
                break;
            case -1:
                cerr << "select fail" << endl;
                break;
            default:
                cout << "get a new link!" << endl;
                HanderEvent(reads);
                break;
            }
        }
    }
    ~SelectServer()
    {
        close(_listensocket);
    }
};
